package com.gitee.wsl.net.ssdp.xml

import com.gitee.wsl.net.ssdp.description.xml.InvalidXmlException
import com.gitee.wsl.net.ssdp.description.xml.XmlElement
import com.gitee.wsl.net.ssdp.description.xml.XmlParser
import org.junit.Assert.assertThrows
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNull

class XmlParserTest {

    @Test
    fun `parse simple xml element`() {
        val xmlElement = """
            <root xmlns="urn:schemas-upnp-org:device-1-0" xmlns:dlna="urn:schemas-dlna-org:device-1-0">
                some content
            </root>
        """.trimIndent()

        val parsed = XmlParser.parse(xmlElement)
        assertEquals("root", parsed.tag)
        assertEquals(2, parsed.attributes?.size)
        assertEquals("urn:schemas-upnp-org:device-1-0", parsed.attributes?.get("xmlns"))
        assertEquals("urn:schemas-dlna-org:device-1-0", parsed.attributes?.get("xmlns:dlna"))
        assertEquals("some content", parsed.value)
    }

    @Test
    fun `fails invalid xml`() {
        val notXmlElement = """
           some random text
        """.trimIndent()

        assertThrows(InvalidXmlException::class.java){
            XmlParser.parse(notXmlElement)
        }
        val malformattedXmlElement = """<root>some content</roo>"""

        assertThrows(InvalidXmlException::class.java){
            XmlParser.parse(malformattedXmlElement)
        }
    }

    val expectedDevice = XmlElement(
        tag = "root", attributes = mapOf("xmlns" to "urn:schemas-upnp-org:device-1-0"), children = listOf(
            XmlElement(
                tag = "specVersion",
                children = listOf(
                    XmlElement(tag = "major", value = "1"),
                    XmlElement(tag = "minor", value = "0")
                )
            ),
            XmlElement(
                tag = "device",
                children = listOf(
                    XmlElement(tag = "deviceType", value = "urn:schemas-upnp-org:device:MediaServer:1"),
                    XmlElement(
                        tag = "dlna:X_DLNADOC",
                        attributes = mapOf("xmlns:dlna" to "urn:schemas-dlna-org:device-1-0"),
                        value = "DMS-1.50"
                    ),
                    XmlElement(tag = "friendlyName", value = "LionKiss_NAS"),
                    XmlElement(tag = "manufacturer", value = "Synology Inc"),
                    XmlElement(tag = "manufacturerURL", value = "http://www.synology.com/"),
                    XmlElement(tag = "modelDescription", value = "Synology DLNA/UPnP Media Server"),
                    XmlElement(tag = "modelName", value = "DS220+"),
                    XmlElement(tag = "modelURL", value = "http://www.synology.com/"),
                    XmlElement(tag = "serialNumber", value = "2280XXXXXXXXXXXXX"),
                    XmlElement(tag = "UDN", value = "uuid:9009d02e-6888-9999-7861-78612ed00990"),
                    XmlElement(tag = "presentationURL", value = "http://192.168.178.33:5050/"),
                    XmlElement(
                        tag = "iconList",
                        children = listOf(
                            XmlElement(
                                tag = "icon",
                                children = listOf(
                                    XmlElement(tag = "mimetype", value = "image/jpeg"),
                                    XmlElement(tag = "width", value = "120"),
                                    XmlElement(tag = "height", value = "120"),
                                    XmlElement(tag = "depth", value = "24"),
                                    XmlElement(tag = "url", value = "/tmp_icon/dmsicon120.jpg")
                                )
                            ),
                            XmlElement(
                                tag = "icon",
                                children = listOf(
                                    XmlElement(tag = "mimetype", value = "image/jpeg"),
                                    XmlElement(tag = "width", value = "48"),
                                    XmlElement(tag = "height", value = "48"),
                                    XmlElement(tag = "depth", value = "24"),
                                    XmlElement(tag = "url", value = "/tmp_icon/dmsicon48.jpg")
                                )
                            ),
                            XmlElement(
                                tag = "icon",
                                children = listOf(
                                    XmlElement(tag = "mimetype", value = "image/png"),
                                    XmlElement(tag = "width", value = "120"),
                                    XmlElement(tag = "height", value = "120"),
                                    XmlElement(tag = "depth", value = "24"),
                                    XmlElement(tag = "url", value = "/tmp_icon/dmsicon120.png")
                                )
                            ),
                            XmlElement(
                                tag = "icon",
                                children = listOf(
                                    XmlElement(tag = "mimetype", value = "image/png"),
                                    XmlElement(tag = "width", value = "48"),
                                    XmlElement(tag = "height", value = "48"),
                                    XmlElement(tag = "depth", value = "24"),
                                    XmlElement(tag = "url", value = "/tmp_icon/dmsicon48.png")
                                )
                            )
                        )
                    ),
                    XmlElement(
                        tag = "serviceList",
                        children = listOf(
                            XmlElement(
                                tag = "service",
                                children = listOf(
                                    XmlElement(
                                        tag = "serviceType",
                                        value = "urn:schemas-upnp-org:service:ConnectionManager:1"
                                    ),
                                    XmlElement(tag = "serviceId", value = "urn:upnp-org:serviceId:ConnectionManager"),
                                    XmlElement(tag = "SCPDURL", value = "/connmgrSCPD.xml"),
                                    XmlElement(tag = "controlURL", value = "/ConnectionManager/control"),
                                    XmlElement(tag = "eventSubURL", value = "/ConnectionManager/event")
                                )
                            ),
                            XmlElement(
                                tag = "service",
                                children = listOf(
                                    XmlElement(
                                        tag = "serviceType",
                                        value = "urn:schemas-upnp-org:service:ContentDirectory:1"
                                    ),
                                    XmlElement(tag = "serviceId", value = "urn:upnp-org:serviceId:ContentDirectory"),
                                    XmlElement(tag = "SCPDURL", value = "/cdsSCPD.xml"),
                                    XmlElement(tag = "controlURL", value = "/ContentDirectory/control"),
                                    XmlElement(tag = "eventSubURL", value = "/ContentDirectory/event")
                                )
                            )
                        )
                    )
                )
            )
        )
    )

    @Test
    fun `parse xml to map`() {
        val sampleDeviceDescription = this::class.java.getResource("/stubs/DeviceDescription.xml")?.readText()
            ?: throw IllegalStateException("Failed to read sample device description")
        assertEquals(expectedDevice, XmlParser.parse(sampleDeviceDescription))
    }

    val expectedRootDevice1 = XmlElement(tag="root", attributes=mapOf("xmlns" to "urn:schemas-upnp-org:device-1-0"), children=listOf(
        XmlElement(tag="specVersion", children=listOf(XmlElement(tag="major", value="1"), XmlElement(tag="minor", value="0"))),
        XmlElement(tag="device", children=listOf(
            XmlElement(tag="deviceType", value="urn:schemas-upnp-org:device:InternetGatewayDevice:1"),
            XmlElement(tag="friendlyName",value="TestDevice 1 Friendly Name"),
            XmlElement(tag="manufacturer", value="AVM Berlin"),
            XmlElement(tag="manufacturerURL", value="http://www.avm.de"),
            XmlElement(tag="modelDescription", value="FRITZ!Box 7590"),
            XmlElement(tag="modelName", value="FRITZ!Box 7590"),
            XmlElement(tag="modelNumber", value="7590avm"),
            XmlElement(tag="modelURL", value="http://www.avm.de"),
            XmlElement(tag="UDN", value="uuid:75802409-bccb-40e7-8e6c-74427FAC04C3"),
            XmlElement(tag="iconList", children=listOf(
                XmlElement(tag="icon", children=listOf(
                    XmlElement(tag="mimetype", value="image/gif"),
                    XmlElement(tag="width", value="118"),
                    XmlElement(tag="height", value="119"),
                    XmlElement(tag="depth", value="8"),
                    XmlElement(tag="url", value="/ligd.gif")
                )),
            )),
            XmlElement(tag="serviceList", children=listOf(
                XmlElement(tag="service", children=listOf(
                    XmlElement(tag="serviceType", value="urn:schemas-any-com:service:Any:1"),
                    XmlElement(tag="serviceId", value="urn:any-com:serviceId:any1"),
                    XmlElement(tag="controlURL", value="/igdupnp/control/any"),
                    XmlElement(tag="eventSubURL", value="/igdupnp/control/any"),
                    XmlElement(tag="SCPDURL", value="/any.xml")
                )),
            )),
            XmlElement(tag="deviceList", children=listOf(
                XmlElement(tag="device", children=listOf(
                    XmlElement(tag="deviceType", value="urn:schemas-upnp-org:device:WANDevice:1"),
                    XmlElement(tag="friendlyName", value="WANDevice - FRITZ!Box 7590"),
                    XmlElement(tag="manufacturer", value="AVM Berlin"),
                    XmlElement(tag="manufacturerURL", value="www.avm.de"),
                    XmlElement(tag="modelDescription", value="WANDevice - FRITZ!Box 7590"),
                    XmlElement(tag="modelName", value="WANDevice - FRITZ!Box 7590"),
                    XmlElement(tag="modelNumber", value="7590avm"),
                    XmlElement(tag="modelURL", value="www.avm.de"),
                    XmlElement(tag="UDN", value="uuid:76802409-bccb-40e7-8e6b-74427FAC04C3"),
                    XmlElement(tag="UPC", value="AVM IGD"),
                    XmlElement(tag="serviceList", children=listOf(
                        XmlElement(tag="service", children=listOf(
                            XmlElement(tag="serviceType", value="urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1"),
                            XmlElement(tag="serviceId", value="urn:upnp-org:serviceId:WANCommonIFC1"),
                            XmlElement(tag="controlURL", value="/igdupnp/control/WANCommonIFC1"),
                            XmlElement(tag="eventSubURL", value="/igdupnp/control/WANCommonIFC1"),
                            XmlElement(tag="SCPDURL", value="/igdicfgSCPD.xml")
                        )),
                    )),
                    XmlElement(tag="deviceList", children=listOf(
                        XmlElement(tag="device", children=listOf(
                            XmlElement(tag="deviceType", value="urn:schemas-upnp-org:device:WANConnectionDevice:1"),
                            XmlElement(tag="friendlyName", value="WANConnectionDevice - FRITZ!Box 7590"),
                            XmlElement(tag="manufacturer", value="AVM Berlin"),
                            XmlElement(tag="manufacturerURL", value="www.avm.de"),
                            XmlElement(tag="modelDescription", value="WANConnectionDevice - FRITZ!Box 7590"),
                            XmlElement(tag="modelName", value="WANConnectionDevice - FRITZ!Box 7590"),
                            XmlElement(tag="modelNumber", value="7590avm"),
                            XmlElement(tag="modelURL", value="www.avm.de"),
                            XmlElement(tag="UDN", value="uuid:76802409-bccb-40e7-8e6a-74427FAC04C3"),
                            XmlElement(tag="UPC", value="AVM IGD"),
                            XmlElement(tag="serviceList", children=listOf(
                                XmlElement(tag="service", children=listOf(
                                    XmlElement(tag="serviceType", value="urn:schemas-upnp-org:service:WANDSLLinkConfig:1"),
                                    XmlElement(tag="serviceId", value="urn:upnp-org:serviceId:WANDSLLinkC1"),
                                    XmlElement(tag="controlURL", value="/igdupnp/control/WANDSLLinkC1"),
                                    XmlElement(tag="eventSubURL", value="/igdupnp/control/WANDSLLinkC1"),
                                    XmlElement(tag="SCPDURL", value="/igddslSCPD.xml")
                                )),
                                XmlElement(tag="service", children=listOf(
                                    XmlElement(tag="serviceType", value="urn:schemas-upnp-org:service:WANIPConnection:1"),
                                    XmlElement(tag="serviceId", value="urn:upnp-org:serviceId:WANIPConn1"),
                                    XmlElement(tag="controlURL", value="/igdupnp/control/WANIPConn1"),
                                    XmlElement(tag="eventSubURL", value="/igdupnp/control/WANIPConn1"),
                                    XmlElement(tag="SCPDURL", value="/igdconnSCPD.xml")
                                )),
                                XmlElement(tag="service", children=listOf(
                                    XmlElement(tag="serviceType", value="urn:schemas-upnp-org:service:WANIPv6FirewallControl:1"),
                                    XmlElement(tag="serviceId", value="urn:upnp-org:serviceId:WANIPv6Firewall1"),
                                    XmlElement(tag="controlURL", value="/igd2upnp/control/WANIPv6Firewall1"),
                                    XmlElement(tag="eventSubURL", value="/igd2upnp/control/WANIPv6Firewall1"),
                                    XmlElement(tag="SCPDURL", value="/igd2ipv6fwcSCPD.xml")
                                ))
                            ))
                        ))
                    ))
                ))
            )),
            XmlElement(tag="presentationURL", value="http://192.168.178.1")
        ))
    ))
    
    @Test
    fun `parse xml with nested devices to map`() {
        val sampleDeviceDescription = this::class.java.getResource("/stubs/RootDevice1Description.xml")?.readText()
            ?: throw IllegalStateException("Failed to read sample device description")
        assertEquals(expectedRootDevice1, XmlParser.parse(sampleDeviceDescription))
    }

    @Test
    fun `cast required xml element to type`() {
        val expected = 123
        val xmlElement = XmlElement(tag = "root", value = expected.toString())
        val actual: Int = xmlElement.get()
        assertEquals(expected, actual)
    }

    @Test
    fun `cast nullable element to required type`() {
        val expected = 123
        val xmlElement = XmlElement(tag = "root", value = expected.toString())
        val actual: Int? = xmlElement.getOrNull()
        assertEquals(expected, actual)
    }

    @Test
    fun `cast nullable empty element to null`() {
        val xmlElement = XmlElement(tag = "root")
        assertNull(xmlElement.getOrNull())
    }
}